use axum::response::IntoResponse;

#[derive(Debug)]
pub enum AppErrorType {
    DbError,
    TplError,
    NotFound,
    RedisError,
    JsonError,
}

#[derive(Debug)]
pub struct AppError {
    pub message: Option<String>,
    pub cause: Option<String>,
    pub error_type: AppErrorType,
}

impl AppError {
    pub fn new(message: Option<String>, cause: Option<String>, error_type: AppErrorType) -> Self {
        Self {
            message,
            cause,
            error_type,
        }
    }
    fn from_err(err: impl ToString, error_type: AppErrorType) -> Self {
        Self::new(None, Some(err.to_string()), error_type)
    }
    fn from_str(msg: &str, error_type: AppErrorType) -> Self {
        Self::new(Some(msg.to_string()), None, error_type)
    }
    pub fn db_error(err: impl ToString) -> Self {
        Self::from_err(err, AppErrorType::DbError)
    }
    pub fn not_found_with_msg(msg: &str) -> Self {
        Self::from_str(msg, AppErrorType::NotFound)
    }
    pub fn not_found_with_option_msg(msg: Option<&str>) -> Self {
        let msg = msg.unwrap_or("没有符合条件的记录");
        Self::not_found_with_msg(msg)
    }
    pub fn not_found() -> Self {
        Self::not_found_with_option_msg(None)
    }
}

impl std::error::Error for AppError {}

impl std::fmt::Display for AppError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl From<deadpool_postgres::PoolError> for AppError {
    fn from(err: deadpool_postgres::PoolError) -> Self {
        Self::db_error(err)
    }
}

impl From<tokio_postgres::Error> for AppError {
    fn from(err: tokio_postgres::Error) -> Self {
        Self::db_error(err)
    }
}

impl From<askama::Error> for AppError {
    fn from(err: askama::Error) -> Self {
        Self::from_err(err, AppErrorType::TplError)
    }
}
impl From<redis::RedisError> for AppError {
    fn from(err: redis::RedisError) -> Self {
        Self::from_err(err, AppErrorType::RedisError)
    }
}
impl From<serde_json::Error> for AppError {
    fn from(err: serde_json::Error) -> Self {
        Self::from_err(err, AppErrorType::JsonError)
    }
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        self.message
            .unwrap_or("发生错误".to_string())
            .into_response()
    }
}
